package com.cdy.server.handler;

import com.cdy.common.command.Command;
import com.cdy.common.command.UploadCommand;
import com.cdy.common.domain.FileEntity;
import com.cdy.common.domain.FileSectionEntity;
import com.cdy.common.property.CommonProperty;
import com.cdy.common.response.UploadResponse;
import com.cdy.common.uitl.BitSetUtil;
import com.cdy.common.uitl.MathUtil;
import com.cdy.store.manager.FileSystem;
import com.cdy.store.repository.FileRepository;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.BitSet;
import java.util.List;

/**
 * 上传命令的处理器
 * <p>
 * Created by 陈东一
 * 2019/10/20 0020 12:29
 */
@Slf4j
public class UploadCommandHandler implements CommandHandler {
    private final FileRepository fileRepository;
    private final FileSystem fileSystem;

    public UploadCommandHandler(FileRepository fileRepository, FileSystem fileSystem) {
        this.fileRepository = fileRepository;
        this.fileSystem = fileSystem;
    }

    @Override
    public void handle(ChannelHandlerContext ctx, Command msg) {
        UploadResponse uploadResponse = new UploadResponse();
        try {
            UploadCommand uploadCommand = (UploadCommand) msg;
            FileEntity fileEntity = fileRepository.getFileByFileMd5(uploadCommand.getFileMD5());

            initUploadResponse(uploadResponse, uploadCommand);

            if (fileEntity == null || fileEntity.getComplete()) { //没有文件 或者完成
                uploadResponse.setComplete(true);
                return;
            }
            Long sectionId = uploadCommand.getSectionId();
            FileSectionEntity sectionEntity = fileRepository.getTaskByIdAndFileId(sectionId, fileEntity.getId());
            if (sectionEntity.getComplete()) {
                uploadResponse.setSuccess(true);
                write(ctx, uploadResponse);
                return;
            }

            File file = fileSystem.findFile(fileEntity.getName() + "-" + sectionEntity.getId());
            BitSet bitSet = sectionEntity.getBitSet();

            long sectionSize = CommonProperty.SmallSectionLength;
            // 确定分片 在分段的位置
            int position = MathUtil.divide(BitSetUtil.correctLong(uploadCommand.getBegin() - uploadCommand.getSectionBegin()), sectionSize) - 1;
            ;
            if (position > sectionEntity.getSectionSize()) { //不合理
                uploadResponse.setSuccess(true);
                write(ctx, uploadResponse);
                return;
            }

            if (!bitSet.get(position)) { //该小片段不存在则写入
                fileSystem.writeFile(file, uploadCommand.getByteBuf().nioBuffer(), uploadCommand.getBegin());
                uploadCommand.getByteBuf().release();
                bitSet.set(position);
                fileRepository.updateTaskById(sectionEntity); // 更新位置信息
            }
            uploadResponse.setSuccess(true);
            // 检测 从分配分段是否上传完成
            Boolean finish = BitSetUtil.sectionFinish(position, uploadCommand.getBitBegin(), uploadCommand.getBitSize(), bitSet);
            if (finish || uploadCommand.getLast()) {
                //全部完成 需要去掉clientId 和 设置完成
                boolean sectionTotalFinish = bitSet.nextClearBit(0) == bitSet.length();
                if (sectionTotalFinish) {
                    sectionEntity.setClientId(null);
                    sectionEntity.setComplete(true);
                    fileRepository.updateTaskById(sectionEntity);
                }
                List<FileSectionEntity> fileSectionEntities = fileRepository.listTaskByFileId(fileEntity.getId());
                boolean fileComplete = fileSectionEntities.stream().allMatch(FileSectionEntity::getComplete);
                if (fileComplete) { // 是否整个文件都上传完成
                    fileEntity.setComplete(true);
                    fileRepository.updateFileByMD5(fileEntity);
                }
                uploadResponse.setComplete(true);
            }
        } catch (Exception e) {
            uploadResponse.setSuccess(false); //上传失败
        }
        write(ctx, uploadResponse);
    }

    /**
     * 初始化上传的响应
     *
     * @param uploadResponse
     * @param uploadCommand
     */
    private void initUploadResponse(UploadResponse uploadResponse, UploadCommand uploadCommand) {
        uploadResponse.setComplete(false);
        uploadResponse.setSuccess(false);
        uploadResponse.setBegin(uploadCommand.getBegin());
        uploadResponse.setBitBegin(uploadCommand.getBitBegin());
        uploadResponse.setBitSize(uploadCommand.getBitSize());
        uploadResponse.setBitTotal(uploadCommand.getBitTotal());
        uploadResponse.setSectionBegin(uploadCommand.getSectionBegin());
        uploadResponse.setLength(uploadCommand.getLength());
    }


}
